﻿using System;
using System.Windows;
using System.Windows.Interactivity;
using System.Windows.Interop;
using nRoute.Navigation;
using nRoute.Navigation.Containers;

namespace nRoute.Behaviors
{
#if (SILVERLIGHT && !WP7)

    public class NavigationShellIntegrationBehavior
		 : Behavior<DependencyObject>
    {
        private const string CONTAINER_NOTFOUND = 
            "Could not resolve INavigationContainer to enabled navigation shell integration (NavigationShellIntegrationBehavior).";
        private const string AREA_URL_FORMAT = "{0};{1}";
        private const string AREA_SEPERATOR = ";";
        private readonly static char[] URL_TRIMS = new char[] { '/' };
        private readonly static char[] AREA_SEPERATORS = new char[] { ';' };

        private INavigationContainer _navigationContainer;
        private string _defaultUrl;

#region Overrides

        protected override void OnAttached()
        {
            if (Application.Current.IsRunningOutOfBrowser) return;

            // we try and resolve our navigation container - either directly or via the handler attached property
            _navigationContainer = NavigationService.GetNavigationContainer(this.AssociatedObject);

            // if we don't have a navigation container then
            if (_navigationContainer == null) throw new InvalidOperationException(CONTAINER_NOTFOUND);

            // we attach handlers to resolve changes
            Application.Current.Host.NavigationStateChanged += Host_NavigationStateChanged;
            _navigationContainer.Navigated += NavigationContainer_Navigated;

            // we also check if we have a navigation state to start with, if so we navigate
            // or if there is no bookmark, we set save the default url as one to show if there is no bookmark
            if (!string.IsNullOrEmpty(Application.Current.Host.NavigationState))
            {
                //_navigationContainer.Url = TrimUrl(Application.Current.Host.NavigationState);
                NavigationService.Navigate(ParseUrlAsNavigationRequest(Application.Current.Host.NavigationState), _navigationContainer);
            }
            else if (!string.IsNullOrEmpty(_navigationContainer.Url))
            {
                _defaultUrl = ParseUrl(_navigationContainer.Url);
            }
        }

        protected override void OnDetaching()
        {
            if (Application.Current.IsRunningOutOfBrowser || _navigationContainer == null) return;

            // we detach
            Application.Current.Host.NavigationStateChanged -= Host_NavigationStateChanged;
            _navigationContainer.Navigated -= NavigationContainer_Navigated;

            // and clear
            _navigationContainer = null;
        }

#endregion

#region Dissmised Handler

        private void Host_NavigationStateChanged(object sender, NavigationStateChangedEventArgs e)
        {
            // we get the new url and check it is not same as the navigation
            var _newUrl = string.IsNullOrEmpty(e.NewNavigationState) ? _defaultUrl : ParseUrl(e.NewNavigationState);
            if (string.IsNullOrEmpty(_newUrl)) return;

            // we check this is not the same
            if (!string.IsNullOrEmpty(_navigationContainer.Url) && string.Equals(
                TrimUrl(_navigationContainer.Url), _newUrl, StringComparison.CurrentCultureIgnoreCase)) return;

            // else we check
            //_navigationContainer.Url = _newUrl;
            NavigationService.Navigate(ParseUrlAsNavigationRequest(_newUrl), _navigationContainer);
        }

        private void NavigationContainer_Navigated(object sender, NavigatedEventArgs e)
        {
            // get the current and navigation url
            var _currentUrl = string.IsNullOrEmpty(Application.Current.Host.NavigationState) ?
                                _defaultUrl : ParseUrl(Application.Current.Host.NavigationState);
            var _navigationUrl = TrimUrl(_navigationContainer.Url);
            
            // if the current bookmark url is not the same as navigation url then set the new bookmark
            if (!string.Equals(_currentUrl, _navigationUrl, StringComparison.CurrentCultureIgnoreCase))
                Application.Current.Host.NavigationState = FormatNavigationRequestAsUrl(e.Request);
        }

#endregion

#region Helpers

        private static string TrimUrl(string url)
        {
            return string.IsNullOrEmpty(url) ? url : url.TrimStart(URL_TRIMS);
        }

        private static NavigationRequest ParseUrlAsNavigationRequest(string url)
        {
            if (!string.IsNullOrEmpty(url) && url.Contains(AREA_SEPERATOR))
            {
                var _areaPart = url.Substring(0, url.IndexOf(AREA_SEPERATOR));
                var _urlPart = url.Substring(url.IndexOf(AREA_SEPERATOR)).TrimStart(AREA_SEPERATORS);
                return new NavigationRequest(_urlPart, _areaPart, NavigateMode.New);
            }
            return new NavigationRequest(url, NavigateMode.New);
        }

        private static string ParseUrl(string url)
        {
            if (!string.IsNullOrEmpty(url) && url.Contains(AREA_SEPERATOR))
            {
                var _urlPart = url.Substring(url.IndexOf(AREA_SEPERATOR)).TrimStart(AREA_SEPERATORS);
                return TrimUrl(_urlPart);
            }
            return TrimUrl(url);
        }

        private static string FormatNavigationRequestAsUrl(NavigationRequest request)
        {
            if (!string.IsNullOrEmpty(request.SiteArea))
            {
                return string.Format(AREA_URL_FORMAT, request.SiteArea, TrimUrl(request.RequestUrl));
            }
            return TrimUrl(request.RequestUrl);
        }

#endregion

    }

#endif

}
